#pragma once
#include <iostream>
#include <mysql.h>
using namespace std;

class MysqlConn
{
public:
    // 初始化数据库
    MysqlConn();
    // 释放数据库连接
    ~MysqlConn();
    // 连接数据库
    bool connect(string user, string passwd, string dbName, string ip, unsigned short port = 3306);
    // 更新数据库: insert, update, delete
    bool update(string sql);
    // 查询数据库
    bool query(string sql);
    // 遍历查询得到的结果集
    bool next(); // 每调用一次next, 就从结果集中取出一条新的记录
    // 得到结果集中的字段值
    string value(int index);

private:
    void freeResult();
private:
    MYSQL* _conn = nullptr;
    MYSQL_RES* _result = nullptr;
    MYSQL_ROW _row = nullptr;
};

MysqlConn::MysqlConn()
{
    _conn = mysql_init(nullptr);
    if (_conn == nullptr)
    {
        cerr << "mysql_init failed" << endl;
        throw runtime_error("mysql_init failed");
    }
}

MysqlConn::~MysqlConn()
{
    if(_conn != nullptr)
    {
        mysql_close(_conn);
    }
    // 释放结果集
    freeResult();
}

bool MysqlConn::connect(string user, string passwd, string dbName, string ip, unsigned short port)
{
    MYSQL* ptr = mysql_real_connect(_conn, ip.c_str(), user.c_str(), passwd.c_str(), dbName.c_str(), port, nullptr, 0);
    if (ptr == nullptr)
    {
        cerr << "mysql_real_connect failed: " << mysql_error(_conn) << endl;
        return false;
    }
    // 设置字符集
    if (mysql_set_character_set(_conn, "utf8"))
    {
        cerr << "mysql_set_character_set failed: " << mysql_error(_conn) << endl;
        return false;
    }
    return true;
}

bool MysqlConn::update(string sql)
{
    if(mysql_query(_conn, sql.c_str()))
    {
        cerr << "mysql_query failed: " << mysql_error(_conn) << endl;
        return false;
    }
    return true;
}

bool MysqlConn::query(string sql)
{
    // 释放之前的结果集
    freeResult();
    if(mysql_query(_conn, sql.c_str()))
    {
        cerr << "mysql_query failed: " << mysql_error(_conn) << endl;
        return false;
    }

    // 保存结果集到客户端
    _result = mysql_store_result(_conn);
    if (_result == nullptr && mysql_errno(_conn))
    {
        cerr << "mysql_store_result failed: " << mysql_error(_conn) << endl;
        return false;
    }
    return true;
}

bool MysqlConn::next()
{
    if(_result != nullptr)
    {
        // 返回的是一个二级指针（指向一级指针，即指针数组，里面都是char*类型），保存了当前这条记录里面所有的字段的值
        _row = mysql_fetch_row(_result);
        if(_row) return true;
    }

    return false;
}

string MysqlConn::value(int index)
{
    // 获取当前记录的字段数量
    int colCount = mysql_num_fields(_result);
    // 检查index是否是有效值
    if(index >= colCount || index < 0)
    {
        return string();
    }

    char* val = _row[index]; // 若取出的字段值是二进制数据，里面可能会有\0，将其转换为字符串只会转换\0前面的，会有问题（需要获取字段值的长度）
    unsigned long length = mysql_fetch_lengths(_result)[index]; // 返回值是一个数组地址（当前记录里面所有字段对应的值的长度）

    return string(val, length);
}

void MysqlConn::freeResult()
{
    if(_result)
    {
        mysql_free_result(_result);
        _result = nullptr;
    }
}